﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace net.xBei.Helper {
    /// <summary>
    /// 
    /// </summary>
    public static class IocHelper {
        /// <summary>
        /// 扫描<paramref name="assemblies"/>并注册所有实现类为作用域
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssemblyTypesAsScoped<TType>(this IServiceCollection services, params Assembly[] assemblies)
            => RegisterAssemblyTypes<TType>(services, ServiceLifetime.Scoped, assemblies);
        /// <summary>
        /// 扫描<paramref name="assemblies"/>并注册所有实现类为单例
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssemblyTypesAsSingleton<TType>(this IServiceCollection services, params Assembly[] assemblies)
            => RegisterAssemblyTypes<TType>(services, ServiceLifetime.Singleton, assemblies);
        /// <summary>
        /// 扫描<paramref name="assemblies"/>并注册所有实现类为瞬态
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssemblyTypesAsTransient<TType>(this IServiceCollection services, params Assembly[] assemblies)
             => RegisterAssemblyTypes<TType>(services, ServiceLifetime.Transient, assemblies);
        /// <summary>
        /// 扫描<paramref name="assemblies"/>并注册所有实现类为<paramref name="config"/>指定的生命周期
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="services"></param>
        /// <param name="config"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssemblyTypes<TType>(this IServiceCollection services, Func<ServiceLifetime> config, params Assembly[] assemblies)
            => RegisterAssemblyTypes<TType>(services, config.Invoke(), assemblies);
        /// <summary>
        /// 扫描<paramref name="assemblies"/>并注册所有实现类为<paramref name="serviceLifetime"/>
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="services"></param>
        /// <param name="serviceLifetime"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssemblyTypes<TType>(this IServiceCollection services, ServiceLifetime serviceLifetime, params Assembly[] assemblies)
            //=> RegisterAssemblyTypes(services, t => !t.IsAbstract && t.IsClass && t.IsAssignableTo<TType>(), serviceLifetime, assemblies);
            => RegisterAssemblyTypes(services, t => !t.IsAbstract && t.IsClass && (typeof(TType)).IsAssignableFrom(t), serviceLifetime, assemblies);

        /// <summary>
        /// 扫描<paramref name="typesFilter"/>并注册所有过滤（<paramref name="typesFilter"/>）以后的类为<paramref name="serviceLifetime"/>
        /// </summary>
        /// <param name="services"></param>
        /// <param name="typesFilter"></param>
        /// <param name="serviceLifetime"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssemblyTypes(this IServiceCollection services, Func<Type, bool> typesFilter, ServiceLifetime serviceLifetime, params Assembly[] assemblies) {
            GetTypes(assemblies, typesFilter)
                .ForEach(type => services.Add(new ServiceDescriptor(type, type, serviceLifetime)));

            return services;
        }

        #region 扫描并注册
        /// <summary>
        /// 扫描并注册 <typeparamref name="TType"/> 的所有实现类为作用域
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="ioc"></param>
        /// <returns></returns>
        public static IocRegisterAssemblyType RegisterAsScoped<TType>(this IocRegisterAssemblyType ioc)
            => RegisterAssemblyTypes<TType>(ioc, ServiceLifetime.Scoped);
        /// <summary>
        /// 扫描并注册 <typeparamref name="TType"/> 的所有实现类为单例
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="ioc"></param>
        /// <returns></returns>
        public static IocRegisterAssemblyType RegisterAsSingleton<TType>(this IocRegisterAssemblyType ioc)
            => RegisterAssemblyTypes<TType>(ioc, ServiceLifetime.Singleton);
        /// <summary>
        /// 扫描并注册 <typeparamref name="TType"/> 的所有实现类为瞬态
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="ioc"></param>
        /// <returns></returns>
        public static IocRegisterAssemblyType RegisterAsTransient<TType>(this IocRegisterAssemblyType ioc)
             => RegisterAssemblyTypes<TType>(ioc, ServiceLifetime.Transient);
        /// <summary>
        /// 扫描并注册 <typeparamref name="TType"/> 的所有实现类为指定的生命周期
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="ioc"></param>
        /// <param name="serviceLifetime"></param>
        /// <returns></returns>
        private static IocRegisterAssemblyType RegisterAssemblyTypes<TType>(this IocRegisterAssemblyType ioc, ServiceLifetime serviceLifetime)
            => RegisterAssemblyTypes(ioc, t => !t.IsAbstract && t.IsClass && (typeof(TType)).IsAssignableFrom(t), serviceLifetime);

        private static IocRegisterAssemblyType RegisterAssemblyTypes(IocRegisterAssemblyType ioc, Func<Type, bool> typesFilter, ServiceLifetime serviceLifetime) {
            if (ioc.Types == null || ioc.Types.Any() == false) {
                return ioc;
            }
            var types = (typesFilter != null ? ioc.Types.Where(typesFilter) : ioc.Types).ToArray();
            foreach (var type in types) {
                //Console.WriteLine($"{serviceLifetime} {type.FullName}");
                ioc.Services.Add(new ServiceDescriptor(type, type, serviceLifetime));
            }
            return ioc;
        }
        /// <summary>
        /// 批量注册实现接口<typeparamref name="TType"/>的服务
        /// </summary>
        /// <typeparam name="TType">必须是一个接口</typeparam>
        /// <param name="ioc"></param>
        /// <returns></returns>
        public static IocRegisterAssemblyType RegisterInterfaceAsScoped<TType>(this IocRegisterAssemblyType ioc)
            => RegisterInterfaceTypes<TType>(ioc, ServiceLifetime.Scoped);
        /// <summary>
        /// 批量注册实现接口<typeparamref name="TType"/>的服务
        /// </summary>
        /// <typeparam name="TType">必须是一个接口</typeparam>
        /// <param name="ioc"></param>
        /// <returns></returns>
        public static IocRegisterAssemblyType RegisterInterfaceAsSingleton<TType>(this IocRegisterAssemblyType ioc)
            => RegisterInterfaceTypes<TType>(ioc, ServiceLifetime.Singleton);
        private static IocRegisterAssemblyType RegisterInterfaceTypes<TType>(this IocRegisterAssemblyType ioc, ServiceLifetime serviceLifetime)
            => RegisterAssemblyTypes(ioc, t => !t.IsAbstract && t.IsClass && (typeof(TType)).IsAssignableFrom(t), typeof(TType), serviceLifetime);
        private static IocRegisterAssemblyType RegisterAssemblyTypes(IocRegisterAssemblyType ioc, Func<Type, bool> typesFilter, Type implementationType, ServiceLifetime serviceLifetime) {
            if (ioc.Types == null || ioc.Types.Any() == false) {
                return ioc;
            }
            var types = typesFilter != null ? ioc.Types.Where(typesFilter) : ioc.Types;
            foreach (var type in types.Distinct().ToArray()) {
                ioc.Services.Add(new ServiceDescriptor(implementationType, type, serviceLifetime));
            }
            return ioc;
        }
        /// <summary>
        /// 开始扫描<paramref name="assemblies"/>并批量注册服务
        /// </summary>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static IocRegisterAssemblyType BeginRegisterServices(this IServiceCollection services, params Assembly?[] assemblies)
            => new(services, assemblies);
        /// <summary>
        /// 扫描<paramref name="assemblies"/>并获取所有类型然后通过<paramref name="typesFilter"/>筛选
        /// </summary>
        /// <param name="assemblies"></param>
        /// <param name="typesFilter"></param>
        /// <returns></returns>
        public static IEnumerable<Type> GetTypes(Assembly?[] assemblies, Func<Type, bool>? typesFilter = null) {
            if (!(assemblies?.Any() == true)) {
                return Array.Empty<Type>();
            }
            var types = assemblies.Select(assembly => assembly?.GetExportedTypes())
                                  .SelectMany(t => t ?? Array.Empty<Type>());
            var typeList = types.ToArray();
            if (typesFilter != null) {
                types = types.Where(typesFilter);
            }
            return types;
        }
        /// <summary>
        /// 
        /// </summary>
        public class IocRegisterAssemblyType {
            internal IocRegisterAssemblyType(IServiceCollection services, Assembly?[] assemblies) {
                Services = services;
                Types = GetTypes(assemblies, t => !t.IsAbstract && t.IsClass);
            }
            /// <summary>
            /// 
            /// </summary>
            public IServiceCollection Services { get; }
            /// <summary>
            /// 
            /// </summary>
            public IEnumerable<Type> Types { get; }
        }
        #endregion
        /// <summary>
        /// 使用反射从<paramref name="obj"/>中获取指定类型（<typeparamref name="TData"/>）的值
        /// </summary>
        /// <typeparam name="TData"></typeparam>
        /// <param name="property"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static TData? GetValue<TData>(this PropertyInfo property, object? obj) {
            return (TData?)property.GetValue(obj);
        }
    }
}
